package com.rengwuxian.coursecoroutines._4_flow

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.EmptyCoroutineContext

/**
 * filter()系列操作符
 * filter() / filterNot() / filterNotNull() / filterIsInstance()
 *
 * Flow操作符概念：是用一个或多个Flow来创建出另一个新的Flow对象的函数。
 * 1.filter {}:过滤符合条件的数据
 * 2.filterNot {}:过滤不符合条件的数据
 * 3.filterNotNull {}:过滤非空数据,过滤掉null；虽然可以用filter多写一次来过滤，但是filter的返回值类型和过滤之前完全一样，下一个操作符还要进行非空判断。
 *                    flow的操作符是不知道的。这个时候用filterNotNull是很方便的。
 *                    flow1.filterNotNull().filter { it % 2 == 0 }.collect { println("1: $it") }
 * 4.filterIsInstance: 给出指定的类型，把符合类型的数据留下来。
 *   一个是范型类型(reified关键字)：flow2.filterIsInstance<List<String>>().collect { println("3: $it") }
 *   一个是参数类型：flow2.filterIsInstance(List::class).collect { println("4: $it") }
 *      参数类型适用于动态确定类型。但是不能使用范型的范型，比如：flow2.filterIsInstance(List<String>::class).collect { println("4: $it") }
 *      范型类型虽然可以指定<>中的类型，但是运行时仍然会被擦除。比如：flow2.filterIsInstance<List<String>>().collect { println("3: $it") }
 *      两者能力范围是一致的。
 *   如果要过滤两层类型，需要这么写：
 *   flow2.filter { it is List<*> && it.firstOrNull()?.let { item -> item is String } == true }
 *       .collect { println("5: $it") }
 *   输出：
 *   5: [A, B]
 */
fun main() = runBlocking<Unit> {
  val scope = CoroutineScope(EmptyCoroutineContext)
  val flow1 = flowOf(1, 2, null, 3, null, 4, 5) // Int?
  val flow2 = flowOf(1, 2, 3, "rengwuxian.com", "扔物线",
    listOf("A", "B"), listOf(1, 2))
  scope.launch {
    flow1.filterNotNull().filter { it % 2 == 0 }.collect { println("1: $it") }
    flow1.filterNotNull().filterNot { it % 2 == 0 }.collect { println("2: $it") }
    flow2.filterIsInstance<List<String>>().collect { println("3: $it") }
    flow2.filterIsInstance(List::class).collect { println("4: $it") }
    flow2.filter { it is List<*> && it.firstOrNull()?.let { item -> item is String } == true }
      .collect { println("5: $it") }
  }
  delay(10000)
}